(function () {
    "use strict"
    // 为对象设置不可枚举的属性
    var define = function define(obj, key, value) {
        Object.defineProperty(obj, key, {
            writable: true,
            configurable: true,
            enumerable: false,
            value: value
        })
    }
    // 确保是 Promise 类的实例
    var ensureInstance = function ensureInstance(obj) {
        if (!(obj instanceof Promise)) throw new TypeError("Method Promise.prototype.then called on incompatible receiver " + obj)
    }
    // 基于定时器模拟 queueMicrotask 创建异步微任务「我们创建的是异步宏任务」
    var queueTask = function queueTask(callback) {
        if (typeof queueMicrotask !== 'undefined') {
            return queueMicrotask(callback)
        }
        var timer = setTimeout(function () {
            callback()
            clearTimeout(timer)
        }, 0)
    }
    // 验证是否为「伪」promise实例
    var isPromise = function isPromise(x) {
        if (x !== null && /^(object|function)$/.test(typeof x)) {
            var then
            try {
                then = x.then
            } catch (err) {
                return false
            }
            if (typeof then === "function") {
                return true
            }
        }
        return false
    }

    /* 构造函数 */
    var Promise = function Promise(executor) {
        var self = this
        if (!(self instanceof Promise)) throw new TypeError("Promise constructor cannot be invoked without 'new'")
        if (typeof executor !== 'function') throw new TypeError("Promise resolver undefined is not a function")

        // 为实例设置私有属性：状态和值
        define(self, 'state', 'pending')
        define(self, 'result', undefined)
        define(self, 'onfulfilledCallback', [])
        define(self, 'onrejectedCallback', [])

        // 修改实例状态和值
        var change = function change(state, result) {
            if (self.state !== 'pending') return
            self.state = state
            self.result = result
            queueTask(function () {
                var callbacks = self.state === 'fulfilled' ? self.onfulfilledCallback : self.onrejectedCallback
                callbacks.forEach(function (callback) {
                    callback(self.result)
                })
            })
        }

        // 立即执行 executor 函数
        try {
            executor(
                function resolve(value) {
                    change('fulfilled', value)
                },
                function reject(reason) {
                    change('rejected', reason)
                }
            )
        } catch (err) {
            change('rejected', err)
        }
    }

    /* 原型对象 */
    var proto = Promise.prototype
    var Resolve = function Resolve(promise, x, resolve, reject) {
        // 根据返回值 x，来决定新返回的实例 promise 的状态和值
        if (x === promise) throw new TypeError("Chaining cycle detected for promise #<Promise>")
        if (x !== null && /^(object|function)$/.test(typeof x)) {
            var then
            try {
                then = x.then
            } catch (err) {
                reject(err)
                return
            }
            if (typeof then === "function") {
                // 返回值 x 是一个 promise 实例「伪实例 或 thenable」
                var called = false
                try {
                    then.call(
                        x,
                        function resolvePromise(y) {
                            if (called) return
                            called = true
                            // resolve(y) 不能直接这样写，因为 x 成功后的值 y，还可能是一个promise实例呢（如果y是一个新实例，则他决定了最终的结果）--> '递归处理'
                            Resolve(promise, y, resolve, reject)
                        },
                        function rejectPromise(r) {
                            if (called) return
                            called = true
                            reject(r)
                        }
                    )
                } catch (err) {
                    if (called) return
                    called = true
                    reject(err)
                }
                return
            }
        }
        // 返回值绝对不是一个promise实例，则让then返回的新实例：状态是成功，值就是返回值x
        resolve(x)
    }
    define(proto, 'then', function then(onfulfilled, onrejected) {
        var self = this,
            promise
        ensureInstance(self)
        // 穿透机制
        if (typeof onfulfilled !== "function") {
            onfulfilled = function (value) {
                return value
            }
        }
        if (typeof onrejected !== "function") {
            onrejected = function (reason) {
                throw reason
            }
        }
        promise = new Promise(function (resolve, reject) {
            switch (self.state) {
                case 'fulfilled':
                    queueTask(function () {
                        try {
                            var x = onfulfilled(self.result)
                            Resolve(promise, x, resolve, reject)
                        } catch (err) {
                            reject(err)
                        }
                    })
                    break
                case 'rejected':
                    queueTask(function () {
                        try {
                            var x = onrejected(self.result)
                            Resolve(promise, x, resolve, reject)
                        } catch (err) {
                            reject(err)
                        }
                    })
                    break
                default:
                    self.onfulfilledCallback.push(function (result) {
                        try {
                            var x = onfulfilled(result)
                            Resolve(promise, x, resolve, reject)
                        } catch (err) {
                            reject(err)
                        }
                    })
                    self.onrejectedCallback.push(function (result) {
                        try {
                            var x = onrejected(result)
                            Resolve(promise, x, resolve, reject)
                        } catch (err) {
                            reject(err)
                        }
                    })
            }
        })
        return promise
    })
    define(proto, 'catch', function (onrejected) {
        var self = this
        ensureInstance(self)
        return self.then(null, onrejected)
    })

    /* 静态私有属性方法 */
    define(Promise, 'resolve', function resolve(x) {
        var promise
        promise = new Promise(function (resolve, reject) {
            Resolve(promise, x, resolve, reject)
        })
        return promise
    })
    define(Promise, 'reject', function reject(x) {
        return new Promise(function (_, reject) {
            reject(x)
        })
    })
    define(Promise, 'all', function all(promises) {
        return new Promise(function (resolve, reject) {
            if (!Array.isArray(promises)) throw new TypeError("promises is not a array")
            var num = 0,
                result = []
            // 如果集合中一项实例都没有，则整体也是成功，值是空数组
            if (promises.length === 0) {
                resolve([])
                return
            }
            // 迭代集合中的每一项，观察每一项的状态和值「每一轮都产生一个闭包」
            promises.forEach(function (promise, index) {
                if (!isPromise(promise)) promise = Promise.resolve(promise)
                promise
                    .then(function (value) {
                        // 有一项为成功，就记录一项
                        num++
                        result[index] = value
                        // 都成功了，则整体返回的实例就是成功
                        if (num >= promises.length) resolve(result)
                    })
                    .catch(function (reason) {
                        // 有一项是失败，则整体返回的实例就是失败
                        reject(reason)
                    })
            })
        })
    })
    define(Promise, 'any', function any(promises) {
        return new Promise(function (resolve, reject) {
            if (!Array.isArray(promises)) throw new TypeError("promises is not a array")
            if (promises.length === 0) {
                reject(new Error('All promises were rejected'))
                return
            }
            var num = 0
            promises.forEach(function (promise) {
                if (!isPromise(promise)) promise = Promise.resolve(promise)
                promise
                    .then(function (value) {
                        resolve(value)
                    })
                    .catch(function (reason) {
                        num++
                        if (num >= promises.length) reject(new Error('All promises were rejected'))
                    })
            })
        })
    })

    /* 测试 */
    Promise.deferred = function deferred() {
        var result = {}
        result.promise = new Promise(function (resolve, reject) {
            result.resolve = resolve
            result.reject = reject
        })
        return result
    }

    /* 暴露API「支持各种环境」 */
    if (typeof window !== 'undefined') window.Promise = Promise
    if (typeof module === 'object' && typeof module.exports === 'object') module.exports = Promise
})();